# 导入需要的模块
# asyncio 是Python的异步I/O框架，用于编写并发代码
import asyncio
# random 用于生成随机数，模拟不同的业务处理时间
import random
# Thread 用于创建单独的线程处理用户输入，避免阻塞异步事件循环
from threading import Thread
# thread_queue 用于在线程和异步代码之间传递用户输入
import queue as thread_queue


# 定义一个简单的异步框架类
class SimpleAsyncFramework:
    # 初始化方法，创建框架实例时调用
    def __init__(self):
        # 创建一个异步队列，用于存储等待办理业务的顾客
        # 队列是一种先进先出的数据结构，符合排队逻辑
        self.waiting_queue = asyncio.Queue()

        # 存储正在办理业务的任务列表，相当于执行器
        self.running_tasks = []

        # 框架运行状态标志，控制无限循环
        self.is_running = False

        # 顾客ID计数器，用于给每个顾客分配唯一标识
        self.customer_id = 0

    # 添加顾客到等待队列的方法
    def add_customer(self, task):
        # 每添加一个顾客，ID计数器加1
        self.customer_id += 1

        # 打印顾客到达的信息
        print(f"\n🔔 顾客 {self.customer_id} 来到窗口前排队")

        # 将顾客ID和对应的业务任务放入等待队列
        # put_nowait 是非阻塞的放入操作
        self.waiting_queue.put_nowait((self.customer_id, task))

        # 返回新添加的顾客ID
        return self.customer_id

    # 处理队列的异步方法，这是框架的核心循环
    async def process_queue(self):
        # 只要框架处于运行状态，就一直循环处理队列
        while self.is_running:
            # 检查队列中是否有等待的顾客
            if not self.waiting_queue.empty():
                # 从队列中取出一位顾客（异步操作，不会阻塞事件循环）
                # await 表示在这里等待操作完成，但不会阻塞整个程序
                customer_id, task = await self.waiting_queue.get()

                # 打印开始办理业务的信息
                print(f"📋 开始为顾客 {customer_id} 办理业务")

                # 创建一个异步任务来处理顾客的业务
                # create_task 会将任务加入事件循环，等待调度执行
                processing_task = asyncio.create_task(
                    self._handle_task(customer_id, task)
                )

                # 将任务添加到正在运行的任务列表（执行器）
                self.running_tasks.append(processing_task)

                # 定义一个回调函数，当任务完成后从执行器中移除
                def remove_task(task):
                    if task in self.running_tasks:
                        self.running_tasks.remove(task)

                # 为任务添加完成回调
                processing_task.add_done_callback(remove_task)

                # 标记队列中的这个任务已经被处理
                self.waiting_queue.task_done()
            else:
                # 如果队列是空的，短暂等待0.5秒再检查
                # 避免无意义的循环占用CPU资源
                await asyncio.sleep(0.5)

    # 处理具体业务的异步方法
    async def _handle_task(self, customer_id, task):
        try:
            # 执行顾客的业务任务（这里的task是一个异步函数）
            # await 会等待业务处理完成
            await task(customer_id)

            # 业务处理完成后打印信息
            print(f"✅ 顾客 {customer_id} 业务办理完成")
        except Exception as e:
            # 捕获并打印业务处理中可能出现的错误
            print(f"❌ 顾客 {customer_id} 业务办理出错: {str(e)}")

    # 启动框架的方法
    async def start(self):
        # 标记框架为运行状态
        self.is_running = True
        print("💼 窗口开始工作，等待顾客...")

        # 启动队列处理循环
        await self.process_queue()

    # 停止框架的方法
    def stop(self):
        # 标记框架为停止状态，结束process_queue中的循环
        self.is_running = False
        print("\n🛑 窗口停止工作")

    # 获取当前等待队列长度的方法
    def get_waiting_count(self):
        return self.waiting_queue.qsize()

    # 获取当前正在办理业务的顾客数量
    def get_processing_count(self):
        return len(self.running_tasks)


# 定义一个普通业务任务（异步函数）
async def normal_business(customer_id):
    # 生成1-3秒的随机处理时间
    process_time = random.uniform(1, 3)

    # 模拟业务处理时间（异步等待，不会阻塞其他任务）
    await asyncio.sleep(process_time)

    # 返回业务处理结果
    return f"普通业务办理完成，耗时 {process_time:.2f} 秒"


# 定义一个快速业务任务
async def quick_business(customer_id):
    # 生成0.5-1.5秒的随机处理时间
    process_time = random.uniform(0.5, 1.5)
    await asyncio.sleep(process_time)
    return f"快速业务办理完成，耗时 {process_time:.2f} 秒"


# 定义一个复杂业务任务
async def complex_business(customer_id):
    # 生成3-5秒的随机处理时间
    process_time = random.uniform(3, 5)
    await asyncio.sleep(process_time)
    return f"复杂业务办理完成，耗时 {process_time:.2f} 秒"


# 处理用户输入的线程函数
def input_thread_function(input_queue):
    # 循环获取用户输入
    while True:
        try:
            # 获取用户输入的命令
            command = input("请输入命令: ")

            # 将命令放入队列，传递给异步代码处理
            input_queue.put(command)

            # 如果输入的是退出命令，结束循环
            if command.lower() == 'e':
                break
        except EOFError:
            # 处理输入结束的情况
            break
        except Exception as e:
            # 处理输入错误
            print(f"输入错误: {e}")


# 处理用户命令的异步函数
async def handle_commands(framework, input_queue):
    # 显示帮助信息
    print("\n📖 命令列表:")
    print("  n - 添加办理普通业务的顾客")
    print("  q - 添加办理快速业务的顾客")
    print("  c - 添加办理复杂业务的顾客")
    print("  s - 查看当前状态")
    print("  e - 退出程序")

    # 循环处理用户命令
    while framework.is_running:
        # 检查是否有用户输入
        if not input_queue.empty():
            # 取出用户输入的命令
            command = input_queue.get()

            # 根据不同命令执行相应操作
            if command.lower() == 'n':
                # 添加普通业务顾客
                framework.add_customer(normal_business)
            elif command.lower() == 'q':
                # 添加快速业务顾客
                framework.add_customer(quick_business)
            elif command.lower() == 'c':
                # 添加复杂业务顾客
                framework.add_customer(complex_business)
            elif command.lower() == 's':
                # 显示当前状态
                print(f"\n📊 当前状态:")
                print(f"  等待办理的顾客: {framework.get_waiting_count()} 人")
                print(f"  正在办理的顾客: {framework.get_processing_count()} 人")
            elif command.lower() == 'e':
                # 退出程序
                print("\n📤 准备退出程序...")
                # 等待所有任务处理完成
                await framework.waiting_queue.join()
                # 等待所有正在处理的任务完成
                if framework.running_tasks:
                    await asyncio.gather(*framework.running_tasks)
                # 停止框架
                framework.stop()
                break
            else:
                # 处理未知命令
                print("❓ 未知命令，请重新输入")

        # 短暂等待，避免过度占用CPU
        await asyncio.sleep(0.1)


# 主函数，程序入口
async def main():
    # 创建框架实例
    framework = SimpleAsyncFramework()

    # 创建一个队列用于传递用户输入
    input_queue = thread_queue.Queue()

    # 创建并启动输入线程
    # 单独的线程处理输入，避免阻塞异步事件循环
    input_thread = Thread(target=input_thread_function, args=(input_queue,), daemon=True)
    input_thread.start()

    # 同时运行框架和命令处理器
    # 创建两个异步任务
    framework_task = asyncio.create_task(framework.start())
    command_task = asyncio.create_task(handle_commands(framework, input_queue))

    # 等待命令处理器完成（用户输入退出命令）
    await command_task

    # 取消框架任务
    framework_task.cancel()
    try:
        await framework_task
    except asyncio.CancelledError:
        # 捕获任务取消的异常，正常处理
        pass

    # 程序结束
    print("🏁 所有业务办理完毕，窗口关闭")


# 如果直接运行这个文件，执行主函数
if __name__ == "__main__":
    try:
        # 运行异步主函数
        asyncio.run(main())
    except KeyboardInterrupt:
        # 处理用户按Ctrl+C中断程序的情况
        print("\n🛑 程序被用户中断")
    except Exception as e:
        # 处理其他可能的错误
        print(f"❌ 程序运行出错: {e}")
